//============================================================================================
/**
 * @file	camera_move.c
 * @brief	カメラ移動
 * @author	GAME FREAK inc.
 */
//============================================================================================
#include "common.h"
#include "camera.h"
#include "system/camera_move.h"

typedef struct CAMERA_MOVE_CNT_tag
{
	GF_CAMERA_PTR CameraPtr;
	u8 TotalSync;		//変更所要シンク数
	u8 NowSync;			//現在シンク数
	u8 Reverse;			//逆再生フラグ
	u8 Req;				//変更リクエスト有無
	CAMERA_MOVE_PARAM	SrcParam;	//変更前のパラメータ
	CAMERA_MOVE_PARAM	DstParam;	//変更後のパラメータ

	TCB_PTR	TaskPtr;		//タスクポインタ

}CAMERA_MOVE_CNT;

static void CamMoveTask( TCB_PTR tcb,void *work );
static void EndRequest(CAM_MOV_CNT_PTR CntPtr);
static void ChangeCamParam(	GF_CAMERA_PTR camera_ptr, CAMERA_MOVE_PARAM *src, CAMERA_MOVE_PARAM *dst,
							const u8 now_sync, const u8 total_sync);
static void ChangeCamAngle(	GF_CAMERA_PTR camera_ptr,  u16 *org, u16 *dst,
							const u8 now_sync, const u8 total_sync );
static void ChangeCamDistance(GF_CAMERA_PTR camera_ptr,  fx32 *org, fx32 *dst,
								const u8 now_sync, const u8 total_sync );
static void ChangeCamPerspWay(GF_CAMERA_PTR camera_ptr,  u16 *org, u16 *dst,
								const u8 now_sync, const u8 total_sync );
static void ShiftCamPos(GF_CAMERA_PTR camera_ptr,
						const VecFx32 *inSrcShift, const VecFx32 *inDstShift,
						const u8 now_sync, const u8 total_sync );
static fx32 GetAddVal(const fx32 inShiftVal, const u8 now_sync, const u8 total_sync);


//==============================================================================
/**
 * カメラコントローラ初期化
 *
 * @param   inHeapID		ヒープＩＤ
 *
 * @retval  cnt_ptr			カメラコントローラポインタ
 */
//==============================================================================
CAM_MOV_CNT_PTR CamMove_Init(const int inHeapID, GF_CAMERA_PTR CameraPtr)
{
	CAM_MOV_CNT_PTR cnt_ptr;
	int size = sizeof(CAMERA_MOVE_CNT);

	cnt_ptr = sys_AllocMemory(inHeapID, size);
	//メモリクリア
	MI_CpuClear8(cnt_ptr, size);

	//カメラポインタセット
	cnt_ptr->CameraPtr = CameraPtr;
	return cnt_ptr;
}

//==============================================================================
/**
 * カメラコントローラ解放
 *
 * @param   cnt_ptr			カメラコントローラポインタ
 *
 * @retval  none
 */
//==============================================================================
void CamMove_Delete(CAM_MOV_CNT_PTR CntPtr)
{
	EndRequest(CntPtr);
	sys_FreeMemoryEz(CntPtr);
}

//==============================================================================
/**
 * カメラ変更リクエスト
 *
 * @param   CntPtr		コントローラポインタ
 * @param	inParam		変更パラメータ
 * @param	inSync		変更に要するシンク数
 *
 * @retval  none
 */
//==============================================================================
void CamMove_Request(CAM_MOV_CNT_PTR CntPtr, const CAMERA_MOVE_PARAM *inParam, const u32 inSync)
{
	if(CntPtr->Req){		//既にリクエストがかかっている場合
		//リクエストをスルー
		return ;
	}else{					//リクエストがかかっていない
		if (inSync == 0){
			return;
		}
		CntPtr->Req = 1;				//変更をリクエスト

		//変更パラメータのセット
		CntPtr->DstParam = *inParam;
		//現在のカメラ設定を保存
		{
			CAMERA_ANGLE ang = GFC_GetCameraAngle(CntPtr->CameraPtr);
			VecFx32 look_at = GFC_GetLookTarget(CntPtr->CameraPtr);
			const VecFx32 *bind = GFC_GetBindTargetVecPtr(CntPtr->CameraPtr);

			CntPtr->SrcParam.Angle = ang.x;
			CntPtr->SrcParam.PerspWay = GFC_GetCameraPerspWay(CntPtr->CameraPtr);
			if (bind != NULL){
				VEC_Subtract( &look_at, bind, &CntPtr->SrcParam.Length);
			}else{
				CntPtr->SrcParam.Length.x = 0;
				CntPtr->SrcParam.Length.y = 0;
				CntPtr->SrcParam.Length.z = 0;
			}
			CntPtr->SrcParam.Distance = GFC_GetCameraDistance(CntPtr->CameraPtr);
		}
		//必要シンク数保存・カウンタクリア
		CntPtr->TotalSync = inSync;
		CntPtr->NowSync = 0;
		//タスク追加
		CntPtr->TaskPtr = TCB_Add(CamMoveTask, CntPtr, 0);
	}
}

//==============================================================================
/**
 * カメラ変更逆再生リクエスト
 * 現在の変更リクエストを逆再生する
 *
 * @param   CntPtr		コントローラポインタ
 *
 * @retval  none
 */
//==============================================================================
void CamMove_RequestRevers(CAM_MOV_CNT_PTR CntPtr)
{
	CntPtr->Reverse = (CntPtr->Reverse+1)%2;
}

//==============================================================================
/**
 * 変更終了チェック
 *
 * @param   CntPtr		コントローラポインタ
 *
 * @retval  u8		0:継続中	0以外：終了
 */
//==============================================================================
const u8 CamMove_CheckFinished(CAM_MOV_CNT_PTR CntPtr)
{
	return !CntPtr->Req;
}

//==============================================================================
/**
 * カメラ移動タスク
 *
 * @param   tcb		タスクポインタ
 * @param	work	ワーク
 *
 * @retval  none
 */
//==============================================================================
static void CamMoveTask( TCB_PTR tcb,void *work )
{
	CAM_MOV_CNT_PTR cnt_ptr = (CAMERA_MOVE_CNT*)work;
	//変更が・・・
	if (!cnt_ptr->Reverse){	//通常再生
		OS_Printf("再生\n");
		//シンクの消化
		cnt_ptr->NowSync++;

		//パラメータ変更
		ChangeCamParam(cnt_ptr->CameraPtr, &cnt_ptr->SrcParam, &cnt_ptr->DstParam,
				cnt_ptr->NowSync, cnt_ptr->TotalSync);

		if (cnt_ptr->NowSync >= cnt_ptr->TotalSync){
			EndRequest(cnt_ptr);							//変更終了
		}
	}else{			//逆再生
		OS_Printf("逆再生\n");
		//パラメータ変更
		ChangeCamParam(cnt_ptr->CameraPtr, &cnt_ptr->DstParam, &cnt_ptr->SrcParam,
				cnt_ptr->TotalSync-cnt_ptr->NowSync, cnt_ptr->TotalSync);

		//シンク消化
		cnt_ptr->NowSync--;
		if (cnt_ptr->NowSync <= 0){
			EndRequest(cnt_ptr);			//変更終了
		}
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	リクエスト終了
 *
 *	@param	CntPtr			コントローラポインタ
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void EndRequest(CAM_MOV_CNT_PTR CntPtr)
{
	if (CntPtr->TaskPtr!=NULL){
		//タスク削除
		TCB_Delete(CntPtr->TaskPtr);
		CntPtr->TaskPtr = NULL;
	}
	CntPtr->TotalSync = 0;
	CntPtr->NowSync = 0;
	CntPtr->Reverse = 0;
	CntPtr->Req = 0;
}

//----------------------------------------------------------------------------
/**
 *	@brief	カメラパラメータ変更
 *
 *	@param	camera_ptr		カメラポインタ
 *	@param	*src			変更前パラメータ
 *	@param	*dst			変更後パラメータ
 *	@param	u8 now_sync		現在シンク
 *	@param	u8 total_sync	総シンク数
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
static void ChangeCamParam(GF_CAMERA_PTR camera_ptr, CAMERA_MOVE_PARAM *src, CAMERA_MOVE_PARAM *dst, const u8 now_sync, const u8 total_sync)
{
	//距離変更
	ChangeCamDistance(camera_ptr,  &src->Distance, &dst->Distance, now_sync, total_sync );
	//アングル変更
	ChangeCamAngle(camera_ptr, &src->Angle, &dst->Angle, now_sync, total_sync );
	//パース変更
	ChangeCamPerspWay(camera_ptr, &src->PerspWay, &dst->PerspWay, now_sync, total_sync );
	//シフト
	ShiftCamPos(camera_ptr, &src->Length, &dst->Length, now_sync, total_sync );
}

//----------------------------------------------------------------------------
/**
 *	@brief	アングル変更関数
 *
 *	@param	camera_ptr		カメラポインタ
 *	@param	*org			変更前パラメータ
 *	@param	*dst			変更後パラメータ
 *	@param	u8 now_sync		現在シンク
 *	@param	u8 total_sync 	総シンク数
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
static void ChangeCamAngle(GF_CAMERA_PTR camera_ptr,  u16 *org, u16 *dst, const u8 now_sync, const u8 total_sync )
{
	u16 def;
	int tmp;
	CAMERA_ANGLE angle = {0,0,0,0};

	//開始アングルと最終目的アングルの差分を取る
	if ( (*dst) >= (*org)  ){
		def = (*dst) - (*org);
		tmp = (def*now_sync)/total_sync;
	}else{
		def = (*org) - (*dst);
		tmp = -( (def*now_sync)/total_sync);
	}

	//現在アングルの計算
	angle.x = (*org) + tmp;

	//アングルセット
	GFC_SetCameraAngleRev(&angle, camera_ptr);
}

//----------------------------------------------------------------------------
/**
 *	@brief	距離変更関数
 *
 *	@param	camera_ptr		カメラポインタ
 *	@param	*org			変更前パラメータ
 *	@param	*dst			変更後パラメータ
 *	@param	u8 now_sync		現在シンク
 *	@param	u8 total_sync 	総シンク数
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
static void ChangeCamDistance(GF_CAMERA_PTR camera_ptr,  fx32 *org, fx32 *dst, const u8 now_sync, const u8 total_sync )
{
	fx32 tmp,def;
	fx32 dis;
	//変更前と変更後の差分を取る
	def = (*dst) - (*org);
	tmp = (def*now_sync)/total_sync;

	//現在距離の計算
	dis = (*org) + tmp;

	//距離セット
	GFC_SetCameraDistance(dis, camera_ptr);
}

//----------------------------------------------------------------------------
/**
 *	@brief	パース変更関数
 *
 *	@param	camera_ptr		カメラポインタ
 *	@param	*org			変更前パラメータ
 *	@param	*dst			変更後パラメータ
 *	@param	u8 now_sync		現在シンク
 *	@param	u8 total_sync 	総シンク数
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
static void ChangeCamPerspWay(GF_CAMERA_PTR camera_ptr,  u16 *org, u16 *dst, const u8 now_sync, const u8 total_sync )
{
	int def;
	int tmp;
	u16 perspway;

	//開始アングルと最終目的アングルの差分を取る
	def = (*dst) - (*org);
	tmp = (def*now_sync)/total_sync;

	//現在パースの計算
	perspway = (*org) + tmp;

	//パースセット
	GFC_SetCameraPerspWay(perspway, camera_ptr);
}

//シフト変更関数
//----------------------------------------------------------------------------
/**
 *	@brief
 *
 *	@param	camera_ptr		カメラポインタ
 *	@param	*inSrcShift		変更前パラメータ
 *	@param	*inDstShift		変更後パラメータ
 *	@param	now_sync		現在シンク
 *	@param	total_sync 		総シンク数
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
static void ShiftCamPos(GF_CAMERA_PTR camera_ptr, const VecFx32 *inSrcShift, const VecFx32 *inDstShift, const u8 now_sync, const u8 total_sync )
{
	VecFx32 now;
	VecFx32 def;

	VEC_Subtract( inDstShift, inSrcShift, &def );

	now.x = GetAddVal(def.x, now_sync, total_sync);
	now.y = GetAddVal(def.y, now_sync, total_sync);
	now.z = GetAddVal(def.z, now_sync, total_sync);

	//シフト
	GFC_ShiftCamera( &now, camera_ptr);
}

//----------------------------------------------------------------------------
/**
 *	@brief	加算値検出
 *
 *	@param	inShiftVal		基準値
 *	@param	now_sync		現在シンク
 *	@param	total_sync		総シンク数
 *
 *	@return fx32	変化値
 */
//-----------------------------------------------------------------------------
static fx32 GetAddVal(const fx32 inShiftVal, const u8 now_sync, const u8 total_sync)
{
	fx32 add_val;
	fx32 old, now;
	GF_ASSERT(now_sync != 0);
	old = ( inShiftVal * (now_sync-1) ) / total_sync;
	now = ( inShiftVal * now_sync) / total_sync;

	add_val = now - old;

	return add_val;
}